#include "Geometry.h"
void DrawAxes(float size)
{
	glPushMatrix();	glPushAttrib(GL_ALL_ATTRIB_BITS);
	for(int i = 0; i < 3; ++i)
	{
		for(int j = 0; j < 2; ++j)
		{
			glLineWidth(((j)?10.0f:.5f));
			glBegin(GL_LINES);
				switch(i)
				{
				case 0: glColor3f(1,0,0); glVertex3f(size*((j)?1:-1),0,0); break;
				case 1: glColor3f(0,1,0); glVertex3f(0,size*((j)?1:-1),0); break;
				case 2: glColor3f(0,0,1); glVertex3f(0,0,size*((j)?1:-1)); break;
				}
				glVertex3f(0,0,0);
			glEnd();
		}
	}
	glPopMatrix();	glPopAttrib();
}

void DrawCube(float size)
{
	DrawCuboid(vec3(size), vec3(-size));
}

void DrawCuboid(vec3 LTF, vec3 RBB)
{
	glBegin(GL_QUADS);

		// Front face
		glNormal3f(0,0,1);
		glVertex3f(LTF.x, LTF.y, LTF.z);
		glVertex3f(LTF.x, RBB.y, LTF.z);
		glVertex3f(RBB.x, RBB.y, LTF.z);
		glVertex3f(RBB.x, LTF.y, LTF.z);

		// Back face
		glNormal3f(0,0,-1);
		glVertex3f(LTF.x, LTF.y, RBB.z);
		glVertex3f(RBB.x, LTF.y, RBB.z);
		glVertex3f(RBB.x, RBB.y, RBB.z);
		glVertex3f(LTF.x, RBB.y, RBB.z);

		// Left Face
		glNormal3f(1,0,0);
		glVertex3f(LTF.x, LTF.y, LTF.z);
		glVertex3f(LTF.x, LTF.y, RBB.z);
		glVertex3f(LTF.x, RBB.y, RBB.z);
		glVertex3f(LTF.x, RBB.y, LTF.z);

		// Right face
		glNormal3f(-1,0,0);
		glVertex3f(RBB.x, LTF.y, LTF.z);
		glVertex3f(RBB.x, RBB.y, LTF.z);
		glVertex3f(RBB.x, RBB.y, RBB.z);
		glVertex3f(RBB.x, LTF.y, RBB.z);

		// Top face
		glNormal3f(0,1,0);
		glVertex3f(LTF.x, LTF.y, LTF.z);
		glVertex3f(RBB.x, LTF.y, LTF.z);
		glVertex3f(RBB.x, LTF.y, RBB.z);
		glVertex3f(LTF.x, LTF.y, RBB.z);

		// Bottom face
		glNormal3f(0,-1,0);
		glVertex3f(LTF.x, RBB.y, LTF.z);
		glVertex3f(LTF.x, RBB.y, RBB.z);
		glVertex3f(RBB.x, RBB.y, RBB.z);
		glVertex3f(RBB.x, RBB.y, LTF.z);

	glEnd();
}

#pragma region spherefuncs

float __x(float phi, float theta, float radius)
{
	phi *= DEGTORAD;
	theta *= DEGTORAD;
	return radius*cos(phi)*cos(theta);
}

float __y(float phi, float theta, float radius)
{
	phi *= DEGTORAD;
	theta *= DEGTORAD;
	return radius*sin(phi);
}

float __z(float phi, float theta, float radius)
{
	phi *= DEGTORAD;
	theta *= DEGTORAD;
	return radius*cos(phi)*sin(theta);
}

#pragma endregion

vec3 GetQuadNormal(vec3 vertex[], short arrsize)
{
	vec3 temp;
	for(int i = 0; i < arrsize; ++i)
	{
		temp += vertex[i] * vertex[(i+1) % arrsize];	// calculate normal vector using cross product
	}

	return temp.normal();	// normalize the vector
}

void DrawSphere (float radius, float slices, float stacks, VBO *vbo)
{
	float stackdiff = 180 / stacks;
	float slicediff = 360 / slices;
	vec3 normal, vertex[4];

	vbo->Begin(GL_QUADS);

		for(float phi = -90; phi < 90; phi += stackdiff)
		{
			for(float theta = 0; theta < 360; theta += slicediff)
			{
				vertex[0](	__x(phi, theta, radius),						// get vertex #1
							__y(phi, theta, radius),
							__z(phi, theta, radius));
				vertex[1](	__x(phi+stackdiff, theta, radius),				// get vertex #2
							__y(phi+stackdiff, theta, radius),
							__z(phi+stackdiff, theta, radius));
				vertex[2](	__x(phi+stackdiff, theta+slicediff, radius),	// get vertex #3
							__y(phi+stackdiff, theta+slicediff, radius),
							__z(phi+stackdiff, theta+slicediff, radius));
				vertex[3](	__x(phi, theta+slicediff, radius),				// get vertex #4
							__y(phi, theta+slicediff, radius),
							__z(phi, theta+slicediff, radius));

				normal = GetQuadNormal(vertex,4);
				
				for(int i = 0; i < 4; ++i)
				{
					vbo->AddVertex(vertex[i].x, vertex[i].y, vertex[i].z);
					vbo->AddNormal(vertex[i].normal());
				}
			}
		}

	vbo->End();
}

void DrawHemisphere (float radius, float slices, float stacks)
{
	float stackdiff = 90 / stacks;
	float slicediff = 360 / slices;
	vec3 normal, vertex[4];

	glBegin(GL_QUADS);

		for(float phi = 0; phi < 90; phi += stackdiff)
		{
			for(float theta = 0; theta < 360; theta += slicediff)
			{
				vertex[0](	__x(phi, theta, radius),						// get vertex #1
							__y(phi, theta, radius),
							__z(phi, theta, radius));
				vertex[1](	__x(phi+stackdiff, theta, radius),				// get vertex #2
							__y(phi+stackdiff, theta, radius),
							__z(phi+stackdiff, theta, radius));
				vertex[2](	__x(phi+stackdiff, theta+slicediff, radius),	// get vertex #3
							__y(phi+stackdiff, theta+slicediff, radius),
							__z(phi+stackdiff, theta+slicediff, radius));
				vertex[3](	__x(phi, theta+slicediff, radius),				// get vertex #4
							__y(phi, theta+slicediff, radius),
							__z(phi, theta+slicediff, radius));

				normal = GetQuadNormal(vertex,4);
				
				for(int i = 0; i < 4; ++i)
				{
					glNormal3f(normal.x, normal.y, normal.z);
					glVertex3f(vertex[i].x, vertex[i].y, vertex[i].z);
				}
			}
		}

	glEnd();
}

void DrawCylinder(float outer_radius, float inner_radius, float height, float slices)
{
	float slicediff = 360 / slices;
	vec3 normal, vertex_outside[4], vertex_inside[4], vertex_cap[4];

	for(float theta = 0; theta < 360; theta += slicediff)
	{
		glBegin(GL_QUADS);	// outside

			vertex_outside[0](	__x(height, theta, outer_radius),				// get vertex #1
								height,	
								__z(height, theta, outer_radius));
			vertex_outside[1](	__x(height, theta+slicediff, outer_radius),		// get vertex #2
								height,	
								__z(height, theta+slicediff, outer_radius));
			vertex_outside[2](	__x(-height, theta+slicediff, outer_radius),	// get vertex #3
								-height,
								__z(-height, theta+slicediff, outer_radius));
			vertex_outside[3](	__x(-height, theta, outer_radius), 				// get vertex #4
								-height,
								__z(-height, theta, outer_radius));

			normal = GetQuadNormal(vertex_outside,4);
			
			for(int i = 0; i < 4; ++i)
			{
				glNormal3f(normal.x, normal.y, normal.z);
				glVertex3f(vertex_outside[i].x, vertex_outside[i].y, vertex_outside[i].z);
			}

		if(inner_radius > 0)
		{
							// inside
			vertex_inside[0](	__x(height, theta, inner_radius),				// get vertex #1
								height,	
								__z(height, theta, inner_radius));
			vertex_inside[1](	__x(-height, theta, inner_radius), 				// get vertex #2
								-height,
								__z(-height, theta, inner_radius));
			vertex_inside[2](	__x(-height, theta+slicediff, inner_radius),	// get vertex #3
								-height,
								__z(-height, theta+slicediff, inner_radius));
			vertex_inside[3](	__x(height, theta+slicediff, inner_radius),		// get vertex #4
								height,	
								__z(height, theta+slicediff, inner_radius));
			
			normal = GetQuadNormal(vertex_inside,4);

			for(int i = 0; i < 4; ++i)
			{
				glNormal3f(normal.x, normal.y, normal.z);
				glVertex3f(vertex_inside[i].x, vertex_inside[i].y, vertex_inside[i].z);
			}
		}
						// top and bottom

		for(int i = 0; i < 2; ++i)
		{
			vertex_cap[0] = vertex_outside[((i)?3:1)];
			vertex_cap[1] = vertex_outside[((i)?2:0)];
			vertex_cap[2](	__x(height*((i)?-1:1), theta, inner_radius),
							height*((i)?-1:1),
							__z(height*((i)?-1:1), theta, inner_radius));
			vertex_cap[3](	__x(height*((i)?-1:1), theta+slicediff*((i)?-1:1), inner_radius),
							height*((i)?-1:1),
							__z(height*((i)?-1:1), theta+slicediff*((i)?-1:1), inner_radius));

			normal = GetQuadNormal(vertex_cap,4);

			for(int j = 0; j < 4; ++j)
			{
				glNormal3f(normal.z, normal.y, normal.z);
				glVertex3f(vertex_cap[j].x, vertex_cap[j].y, vertex_cap[j].z);
			}
		}

	glEnd();
	}
}

void DrawCuboidSmooth(vec3 ltf, vec3 rbb, int div)
{
	float width = ltf.x - rbb.x;
	float height = ltf.y - rbb.y;
	float length = ltf.z - rbb.z;

	div++;
	float Xsize, Ysize, Zsize;
	Xsize = Ysize = Zsize = div;

	// TOP and BOTTOM
	for(int i = 0; i < Xsize; ++i)
	{
		for(int j = 0; j < Zsize; j++)
		{
			glPushMatrix();

				glTranslatef(-i * (width/Xsize), 0, -j * (length/Zsize));

				glBegin(GL_QUADS);

					// top
					glNormal3f(0,1,0); 
					glVertex3f(ltf.x,					ltf.y,	ltf.z);
					glVertex3f(ltf.x,					ltf.y,	ltf.z - (length/Zsize));
					glVertex3f(ltf.x - (width/Xsize),	ltf.y,	ltf.z - (length/Zsize));
					glVertex3f(ltf.x - (width/Xsize),	ltf.y,	ltf.z);

					// bottom
					glNormal3f(0,-1,0); 
					glVertex3f(ltf.x,					rbb.y,	ltf.z);
					glVertex3f(ltf.x - (width/Xsize),	rbb.y,	ltf.z);
					glVertex3f(ltf.x - (width/Xsize),	rbb.y,	ltf.z - (length/Zsize));
					glVertex3f(ltf.x,					rbb.y,	ltf.z - (length/Zsize));

				glEnd();

			glPopMatrix();
		}
	}

	// FRONT and BACK
	for(int i = 0; i < Xsize; ++i)
	{
		for(int j = 0; j < Ysize; j++)
		{
			glPushMatrix();

				glTranslatef(-i * (width/Xsize), -j * (height/Ysize), 0);

				glBegin(GL_QUADS);

					// front
					glNormal3f(0,0,1);
					glVertex3f(ltf.x,					ltf.y,					ltf.z);
					glVertex3f(ltf.x - (width/Xsize),	ltf.y,					ltf.z);
					glVertex3f(ltf.x - (width/Xsize),	ltf.y - (height/Ysize),	ltf.z);
					glVertex3f(ltf.x,					ltf.y - (height/Ysize),	ltf.z);

					// back
					glNormal3f(0,0,-1);
					glVertex3f(ltf.x,					ltf.y,					rbb.z);
					glVertex3f(ltf.x,					ltf.y - (height/Ysize),	rbb.z);
					glVertex3f(ltf.x - (width/Xsize),	ltf.y - (height/Ysize),	rbb.z);
					glVertex3f(ltf.x - (width/Xsize),	ltf.y,					rbb.z);

				glEnd();

			glPopMatrix();
		}
	}

	// LEFT and RIGHT
	for(int i = 0; i < Ysize; ++i)
	{
		for(int j = 0; j < Zsize; j++)
		{
			glPushMatrix();

				glTranslatef(0, -i * (height/Ysize), -j * (length/Zsize));

				glBegin(GL_QUADS);

					// left
					glNormal3f(1,0,0);
					glVertex3f(ltf.x,	ltf.y,					ltf.z);
					glVertex3f(ltf.x,	ltf.y - (height/Ysize),	ltf.z);
					glVertex3f(ltf.x,	ltf.y - (height/Ysize),	ltf.z - (length/Zsize));
					glVertex3f(ltf.x ,	ltf.y,					ltf.z - (length/Zsize));

					// right
					glNormal3f(-1,0,0);
					glVertex3f(rbb.x,	ltf.y,					ltf.z);
					glVertex3f(rbb.x ,	ltf.y,					ltf.z - (length/Zsize));
					glVertex3f(rbb.x,	ltf.y - (height/Ysize),	ltf.z - (length/Zsize));
					glVertex3f(rbb.x,	ltf.y - (height/Ysize),	ltf.z);

				glEnd();

			glPopMatrix();
		}
	}
}

void DrawWallsSmooth(vec3 ltf, vec3 rbb, int div, VBO *vbo)
{
	float width = ltf.x - rbb.x;
	float height = ltf.y - rbb.y;
	float length = ltf.z - rbb.z;

	div++;
	float Xsize, Ysize, Zsize;
	Xsize = Ysize = Zsize = div;

	vec3 temp_ltf, temp_rbb;

	vbo->Begin(GL_QUADS);

	// FRONT and BACK
	for(int i = 0; i < Xsize; ++i)
	{
		for(int j = 0; j < Ysize; j++)
		{
		//	glPushMatrix();

		//		glTranslatef(-i * (width/Xsize), -j * (height/Ysize), 0);
				temp_ltf = ltf + vec3(-i * (width/Xsize), -j * (height/Ysize), 0);
				temp_rbb = rbb + vec3(-i * (width/Xsize), -j * (height/Ysize), 0);

		//		glBegin(GL_QUADS);

					// front
					vbo->AddVertex(temp_ltf.x,					temp_ltf.y,						temp_ltf.z);
					vbo->AddNormal(0,0,-1);
					vbo->AddVertex(temp_ltf.x,					temp_ltf.y - (height/Ysize),	temp_ltf.z);
					vbo->AddNormal(0,0,-1);
					vbo->AddVertex(temp_ltf.x - (width/Xsize),	temp_ltf.y - (height/Ysize),	temp_ltf.z);
					vbo->AddNormal(0,0,-1);
					vbo->AddVertex(temp_ltf.x - (width/Xsize),	temp_ltf.y,						temp_ltf.z);
					vbo->AddNormal(0,0,-1);

					// back
					vbo->AddVertex(temp_ltf.x,					temp_ltf.y,						temp_rbb.z);
					vbo->AddNormal(0,0,1);
					vbo->AddVertex(temp_ltf.x - (width/Xsize),	temp_ltf.y,						temp_rbb.z);
					vbo->AddNormal(0,0,1);
					vbo->AddVertex(temp_ltf.x - (width/Xsize),	temp_ltf.y - (height/Ysize),	temp_rbb.z);
					vbo->AddNormal(0,0,1);
					vbo->AddVertex(temp_ltf.x,					temp_ltf.y - (height/Ysize),	temp_rbb.z);
					vbo->AddNormal(0,0,1);

		//		glEnd();

		//	glPopMatrix();
		}
	}

	vbo->End();

	vbo->Begin(GL_QUADS);

	// LEFT and RIGHT
	for(int i = 0; i < Ysize; ++i)
	{
		for(int j = 0; j < Zsize; j++)
		{
//			glPushMatrix();

//				glTranslatef(0, -i * (height/Ysize), -j * (length/Zsize));
				temp_ltf = ltf + vec3(0, -i * (height/Ysize), -j * (length/Zsize));
				temp_rbb = rbb + vec3(0, -i * (height/Ysize), -j * (length/Zsize));
//
//				glBegin(GL_QUADS);

					// left
					vbo->AddVertex(temp_ltf.x,	temp_ltf.y,						temp_ltf.z);
					vbo->AddNormal(-1,0,0);
					vbo->AddVertex(temp_ltf.x ,	temp_ltf.y,						temp_ltf.z - (length/Zsize));
					vbo->AddNormal(-1,0,0);
					vbo->AddVertex(temp_ltf.x,	temp_ltf.y - (height/Ysize),	temp_ltf.z - (length/Zsize));
					vbo->AddNormal(-1,0,0);
					vbo->AddVertex(temp_ltf.x,	temp_ltf.y - (height/Ysize),	temp_ltf.z);
					vbo->AddNormal(-1,0,0);

					// right
					vbo->AddVertex(temp_rbb.x,	temp_ltf.y,						temp_ltf.z);
					vbo->AddNormal(1,0,0);
					vbo->AddVertex(temp_rbb.x,	temp_ltf.y - (height/Ysize),	temp_ltf.z);
					vbo->AddNormal(1,0,0);
					vbo->AddVertex(temp_rbb.x,	temp_ltf.y - (height/Ysize),	temp_ltf.z - (length/Zsize));
					vbo->AddNormal(1,0,0);
					vbo->AddVertex(temp_rbb.x ,	temp_ltf.y,						temp_ltf.z - (length/Zsize));
					vbo->AddNormal(1,0,0);

//				glEnd();

//			glPopMatrix();
		}
	}

	vbo->End();
}

void DrawFloorSmooth(vec3 ltf, vec3 rbb, int div, VBO *vbo)
{
	float width = ltf.x - rbb.x;
	float height = ltf.y - rbb.y;
	float length = ltf.z - rbb.z;

	div++;
	float Xsize, Ysize, Zsize;
	Xsize = Ysize = Zsize = div;
	
	vec3 temp_ltf, temp_rbb;

	vbo->Begin(GL_QUADS);

	// TOP and BOTTOM
	for(int i = 0; i < Xsize; ++i)
	{
		for(int j = 0; j < Zsize; j++)
		{
//			glPushMatrix();

//				glTranslatef(-i * (width/Xsize), 0, -j * (length/Zsize));
				temp_ltf = ltf + vec3(-i * (width/Xsize), 0, -j * (length/Zsize));
				temp_rbb = rbb + vec3(-i * (width/Xsize), 0, -j * (length/Zsize));

//				glBegin(GL_QUADS);

					// bottom
					vbo->AddVertex(temp_ltf.x,					temp_rbb.y,	temp_ltf.z);
					vbo->AddNormal(0,1,0); 
					vbo->AddVertex(temp_ltf.x,					temp_rbb.y,	temp_ltf.z - (length/Zsize));
					vbo->AddNormal(0,1,0); 
					vbo->AddVertex(temp_ltf.x - (width/Xsize),	temp_rbb.y,	temp_ltf.z - (length/Zsize));
					vbo->AddNormal(0,1,0); 
					vbo->AddVertex(temp_ltf.x - (width/Xsize),	temp_rbb.y,	temp_ltf.z);
					vbo->AddNormal(0,1,0); 

//				glEnd();

//			glPopMatrix();
		}
	}

	vbo->End();
}

void DrawCeilSmooth(vec3 ltf, vec3 rbb, int div)
{
	float width = ltf.x - rbb.x;
	float height = ltf.y - rbb.y;
	float length = ltf.z - rbb.z;

	div++;
	float Xsize, Ysize, Zsize;
	Xsize = Ysize = Zsize = div;

	// TOP and BOTTOM
	for(int i = 0; i < Xsize; ++i)
	{
		for(int j = 0; j < Zsize; j++)
		{
			glPushMatrix();

				glTranslatef(-i * (width/Xsize), 0, -j * (length/Zsize));

				glBegin(GL_QUADS);

					// top
					glNormal3f(0,-1,0); 
					glVertex3f(ltf.x,					ltf.y,	ltf.z);
					glVertex3f(ltf.x,					ltf.y,	ltf.z - (length/Zsize));
					glVertex3f(ltf.x - (width/Xsize),	ltf.y,	ltf.z - (length/Zsize));
					glVertex3f(ltf.x - (width/Xsize),	ltf.y,	ltf.z);

				glEnd();

			glPopMatrix();
		}
	}
}